package org.dizem.freepl0.compiler.targetcodemanager;

import org.dizem.freepl0.compiler.*;
import org.dizem.freepl0.compiler.errormanager.ErrorManager;
import org.dizem.freepl0.gui.panel.OutputPanel;

/**
 * Created by IntelliJ IDEA.
 * User: DIZEM
 * Date: 11-2-2
 * Time: 下午3:04
 */
public class TargetCodeManager {
	public Instruction[] code = new Instruction[CODE_SIZE];
	private int codeIndex = 0;

	private int baseAddress;
	private int[] stack = new int[RUN_STACK_SIZE];

	public static int CODE_SIZE = 200;
	public static int RUN_STACK_SIZE = 500;

	ErrorManager errorManager;


	public TargetCodeManager(FPCompiler compiler) {

		errorManager = compiler.getErrorManager();
		for (int i = 0; i < CODE_SIZE; ++i) {
			code[i] = new Instruction();
		}
	}

	public void addCode(Fct fct, int level, int address) {

		if (codeIndex < CODE_SIZE) {
			code[codeIndex].setFct(fct);
			code[codeIndex].setLevel(level);
			code[codeIndex++].setAddress(address);

		} else {
			errorManager.addError(27);
		}
	}

	public String listCode(int i) {
		return code[i].getFct() + "\t " + code[i].getLevel() + "\t " + code[i].getAddress();
	}

	public int getCodeIndex() {
		return codeIndex;
	}

	public void run(OutputPanel panel) {
		Fct fct;
		int level, address;
		int t = 0;
		baseAddress = 1;
		int index = 0;
		stack[1] = 0;
		stack[2] = 0;
		stack[3] = 0;

		do {

			fct = code[index].getFct();
			level = code[index].getLevel();
			address = code[index].getAddress();
			index++;

			switch (fct) {

				case LIT:
					stack[++t] = address;
					break;

				case OPR:

					switch (address) {
						case 0:
							t = baseAddress - 1;
							index = stack[t + 3];
							baseAddress = stack[t + 2];
							break;

						case 1:
							stack[t] = -stack[t];
							break;

						case 2:
							t--;
							stack[t] = stack[t] + stack[t + 1];
							break;

						case 3:
							t--;
							stack[t] = stack[t] - stack[t + 1];
							break;

						case 4:
							t--;
							stack[t] = stack[t] * stack[t + 1];
							break;

						case 5:
							t--;
							stack[t] = stack[t] / stack[t + 1];
							break;

						case 6:
							stack[t] %= 2;
							break;

						case 8:
							t--;
							stack[t] = stack[t] == stack[t + 1] ? 1 : 0;
							break;

						case 9:
							t--;
							stack[t] = stack[t] != stack[t + 1] ? 1 : 0;
							break;

						case 10:
							t--;
							stack[t] = stack[t] < stack[t + 1] ? 1 : 0;
							break;

						case 11:
							t--;
							stack[t] = stack[t] >= stack[t + 1] ? 1 : 0;
							break;

						case 12:
							t--;
							stack[t] = stack[t] > stack[t + 1] ? 1 : 0;
							break;

						case 13:
							t--;
							stack[t] = stack[t] <= stack[t + 1] ? 1 : 0;
							break;

						case 14:
							panel.appendText(stack[t] + "\n");
							break;

						case 16:
							// todo: stack[t] = getValue();
							break;
					}
					break;

				case LOD:
					t++;
					stack[t] = stack[getBasement(level) + address];
					break;

				case STO:
					stack[getBasement(level) + address] = stack[t];
					t--;
					break;

				case CAL:
					stack[t + 1] = getBasement(level);
					stack[t + 2] = baseAddress;
					stack[t + 3] = index;
					baseAddress = t + 1;
					index = address;
					break;

				case INT:
					t += address;
					break;

				case JMP:
					index = address;
					break;

				case JPC:
					if (stack[t] == 0)
						index = address;
					t--;
					break;
			}
		} while (index != 0);
	}


	private int getBasement(int l) {
		int b = baseAddress;
		while (l > 0) {
			b = stack[b];
			l--;
		}
		return b;
	}
}